﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace Morro.VPN.Desktop.DataDisplay
{
    public abstract class BaseGraph : Canvas
    {
        //Ширина 
        private double _width;
        //Высота
        private double _height;
        //Интервал по Х для сетки
        private double _lineIntervalX;
        //Интервал по У для сетки
        private double _lineIntervalY;

        ////Пределы графика, для осей
        //private double _measurementMinX;
        //private double _measurementMinY;
        //private double _measurementMaxX;
        //private double _measurementMaxY;

        //Перо сетки
        private Pen _gridLinePen;
        //Жирное перо для сетки
        private Pen _gridLinePenBold;
        //Кисть фона
        private Brush _backgroundBrush;
        //Перо осей
        private Pen _axisPen;
        //Перо для рамки
        private Pen _borderPen;

        //Отступ от рамки
        private double _margin = 10;

        //Показывать рамку
        private bool _showBorder = true;

        //Точки на оси координат, которые нужно подписать
        protected double[] _snapAsisXPoints;
        protected double[] _snapAsisYPoints;

        private Label _graphCaption;


        protected void UpdateUICore()
        {
            _width = this.ActualWidth;
            _height = this.ActualHeight;

            this.InvalidateVisual();
        }

        ////Нулевая точка (относительнок которой происходи обсчет)
        //private Point _nullPoint;

        ////Точка на графике (не на канвасе)
        //private Point _nullPointGraph;

        public BaseGraph()
        {
            _lineIntervalX = 10;
            _lineIntervalY = 10;

            Brush gridLineBrush = new SolidColorBrush(Colors.Linen);
            gridLineBrush.Opacity = 0.3;
            _gridLinePen = new Pen(gridLineBrush, 1);

            Brush gridLineBrushBold = new SolidColorBrush(Colors.Linen);
            gridLineBrushBold.Opacity = 0.6;
            _gridLinePenBold = new Pen(gridLineBrushBold, 1);

            _backgroundBrush = new SolidColorBrush(Colors.Black);

            _axisPen = new Pen(new SolidColorBrush(Colors.Red), 1);

            _borderPen = new Pen(Brushes.White, 1);

            this.Loaded += new System.Windows.RoutedEventHandler(BaseGraph_Loaded);
            this.SizeChanged += new SizeChangedEventHandler(BaseGraph_SizeChanged);

            _graphCaption = new Label();
            _graphCaption.Foreground = new SolidColorBrush(Colors.Yellow);
            this.Children.Add(_graphCaption);
            Canvas.SetLeft(_graphCaption, 50);
            Canvas.SetTop(_graphCaption, 10);
        }

        public void SetParameters(double intervalX, double IntervalY)
        {
            _lineIntervalX = intervalX;
            _lineIntervalY = IntervalY;

            UpdateUICore();
        }

        public void SetCaption(string text)
        {
            _graphCaption.Content = text;
        }

        #region Draw Area Size

        /// <summary>
        /// Левая вершина области рисования
        /// </summary>
        protected double DrawAreaLeft { get { return _margin; } }

        /// <summary>
        /// Вершина области рисования
        /// </summary>
        protected double DrawAreaTop { get { return _margin; } }

        /// <summary>
        /// Ширина области рисования
        /// </summary>
        protected double DrawAreaWidth { get { return _width - 2 * _margin; } }

        /// <summary>
        /// Высота области рисования
        /// </summary>
        protected double DrawAreaHeight { get { return _height - 2 * _margin; } }

        /// <summary>
        /// Нижняя точка плоскости рисования
        /// </summary>
        protected double DrawAreaBottom { get { return DrawAreaTop + DrawAreaHeight; } }

        /// <summary>
        /// Правая точка области рисования
        /// </summary>
        protected double DrawAreaRight { get { return DrawAreaLeft + DrawAreaWidth; } }

        #endregion


        //protected Point TranslateScattegram(double x, double y, double min, double max)
        //{
        //    double pRRX = (x - min) / (max - min);
        //    double pRRY = (y - min) / (max - min);



        //    double xValue = pRRX * (_width - _nullPoint.X - _margin * 2) + _margin + _nullPoint.X;
        //    double yValue = pRRY * (_height - (_height - _nullPoint.Y) - _margin * 2) + _margin + (_height - _nullPoint.Y);

        //    yValue = _height - yValue;
        //    return new Point(xValue, yValue);
        //}

        //protected void DrawAsix(string[] stringArray, DrawingContext dc)
        //{

        //    double labelSteps = (_width - _nullPoint.X) / stringArray.Length;

        //    for (int i = 0; i < stringArray.Length; i++)
        //    {

        //        FormattedText text = new FormattedText(stringArray[i],
        //                                               CultureInfo.GetCultureInfo("ru-RU"),
        //                                               FlowDirection.LeftToRight,
        //                                               new Typeface("Verdana"),
        //                                               12, Brushes.White);

        //        Point p = new Point(labelSteps * i + _nullPoint.X + 7 + _margin, _nullPoint.Y - text.Height - 1 - _margin);

        //        // Draw a formatted text string into the DrawingContext.
        //        dc.DrawText(text, p);

        //        dc.DrawEllipse(Brushes.Green, null, new Point(labelSteps * i + _nullPoint.X + _margin, _nullPoint.Y - _margin), 3, 3);
        //    }
        //}

        //public void SetNullPoint(Point x)
        //{
        //    _nullPoint = x;
        //    this.InvalidateVisual();
        //}

        public void SetMargin(int margin)
        {
            this._margin = margin;
            UpdateUICore();
        }

        void BaseGraph_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            //_width = e.NewSize.Width;
            //_height = e.NewSize.Height;

            //_nullPoint = new Point(_lineIntervalX, _height - _lineIntervalY);

            //_nullPointGraph = new Point(DrawAreaWidth / 2, DrawAreaHeight / 2);

            UpdateUICore();
            this.Clip = new RectangleGeometry(new Rect(0, 0, _width, _height));
            //this.Clip = new RectangleGeometry(new Rect(_nullPoint.X-2, 0, _width - _nullPoint.X+2, _nullPoint.Y+2));


        }

        void BaseGraph_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            UpdateUICore();
        }

        public virtual void DrawAsix(DrawingContext dc)
        {
            //Point oX1 = TranslateToCanvas(_nullPointGraph.X, 0);
            //Point oX2 = TranslateToCanvas(_nullPointGraph.X, DrawAreaHeight);

            //Point oY1 = TranslateToCanvas(0, _nullPointGraph.Y);
            //Point oy2 = TranslateToCanvas(DrawAreaWidth, _nullPointGraph.Y);


            //dc.DrawLine(_axisPen, oX1, oX2);
            //dc.DrawLine(_axisPen, oY1, oy2);
        }

        /// <summary>
        /// Рисование графиков и сетки
        /// </summary>
        protected override void OnRender(DrawingContext dc)
        {

            #region Grid
            dc.DrawRectangle(_backgroundBrush, null, new Rect(0, 0, _width, _height));

            //Рамка
            if (ShowBorder)
            {
                dc.DrawLine(_borderPen, new Point(DrawAreaLeft, DrawAreaTop), new Point(DrawAreaRight, DrawAreaTop));
                dc.DrawLine(_borderPen, new Point(DrawAreaRight, DrawAreaTop), new Point(DrawAreaRight, DrawAreaBottom));
                dc.DrawLine(_borderPen, new Point(DrawAreaLeft, DrawAreaBottom),
                            new Point(DrawAreaRight, DrawAreaBottom));
                dc.DrawLine(_borderPen, new Point(DrawAreaLeft, DrawAreaTop), new Point(DrawAreaLeft, DrawAreaBottom));
            }


            int linesX = (int)(DrawAreaWidth / _lineIntervalX) + 1;
            int linesY = (int)(DrawAreaHeight / _lineIntervalY) + 1;

            List<double> pointsY = new List<double>();
            for (int i = 0; i < linesY; i++)
            {
                double perem = _lineIntervalY * i;
                Point pStart = new Point(0, perem);
                Point pEnd = new Point(DrawAreaWidth, perem);

                if (i % 5 != 0)
                    dc.DrawLine(_gridLinePen, TranslateToCanvas(pStart.X, pStart.Y), TranslateToCanvas(pEnd.X, pEnd.Y));
                else
                {
                    dc.DrawLine(_gridLinePenBold, TranslateToCanvas(pStart.X, pStart.Y), TranslateToCanvas(pEnd.X, pEnd.Y));
                    pointsY.Add(pStart.Y);
                }
            }


            List<double> pointsX = new List<double>();
            for (int i = 0; i < linesX; i++)
            {
                double perem = _lineIntervalX * i;
                Point pStart = new Point(perem, 0);
                Point pEnd = new Point(perem, DrawAreaHeight);

                if (i % 5 != 0)
                    dc.DrawLine(_gridLinePen, TranslateToCanvas(pStart.X, pStart.Y), TranslateToCanvas(pEnd.X, pEnd.Y));
                else
                {
                    dc.DrawLine(_gridLinePenBold, TranslateToCanvas(pStart.X, pStart.Y), TranslateToCanvas(pEnd.X, pEnd.Y));
                    pointsX.Add(pStart.X);
                }
            }

            _snapAsisXPoints = pointsX.ToArray();
            _snapAsisYPoints = pointsY.ToArray();


            #endregion


            #region Asix
            DrawAsix(dc);
            #endregion
        }

        /// <summary>
        /// Переводит из координат графика в координаты канваса (0.0 - в левом углу не считая маргин)
        /// </summary>
        protected Point TranslateToCanvas(double x, double y)
        {
            Point p = new Point();
            p.X = x + DrawAreaLeft;
            p.Y = DrawAreaBottom - y;
            return p;
        }

        /// <summary>
        /// Переводит в координаты графика. 0.0 в нуллПоинт считая маргин
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        protected Point TranslateToGraph(double x, double y)
        {
            Point p = new Point();
            p.X = x - DrawAreaLeft;
            p.Y = y + DrawAreaBottom;// -y - DrawAreaTop;
            return p;
        }

        /// <summary>
        /// Для отладки
        /// </summary>
        [Obsolete]
        private void DrawSinus(DrawingContext dc)
        {
            // В это нужно вписать
            double x1 = DrawAreaLeft;
            double y1 = DrawAreaTop;
            double x2 = DrawAreaWidth;
            double y2 = DrawAreaHeight;

            double intervalX = (x2 - x1) / 360.0;
            double intervalY = (y2 - y1) / 2;

            double yOffcet = -intervalY - DrawAreaTop;

            List<Point> _sinPoints = new List<Point>();
            for (int i = 1; i < 359; i++)
            {
                Point p = new Point();
                double rad = i * Math.PI / 180.0;

                p.X = intervalX * i + DrawAreaLeft;


                p.Y = intervalY * Math.Sin(rad) + yOffcet;

                _sinPoints.Add(p);
            }


            StreamGeometry streamGeometry = new StreamGeometry();
            using (StreamGeometryContext context = streamGeometry.Open())
            {
                context.BeginFigure(_sinPoints[0], false, false);
                context.PolyLineTo(_sinPoints, true, true);
            }

            streamGeometry.Freeze();

            dc.PushTransform(new TranslateTransform(0, _height));
            dc.DrawGeometry(Brushes.Black, new Pen(Brushes.Green, 5), streamGeometry);
            dc.Pop();
        }

        #region Draw Instruments
        /// <summary>
        /// Перо для рисования осей
        /// </summary>
        protected Pen AsisPen { get { return _axisPen; } }
        #endregion

        #region Properties
        /// <summary>
        /// Показывать рамку
        /// </summary>
        public bool ShowBorder
        {
            get { return _showBorder; }
            set
            {
                if (_showBorder != value)
                {
                    _showBorder = value;
                    UpdateUICore();
                }
            }
        }
        #endregion
    }
}
